using System;
using AsyncLibrary.StatusListener;
using log4net;

namespace AsyncLibrary
{
  public abstract class AsyncOperation : IAsyncOperation
  {
    private static readonly ILog Log = LogManager.GetLogger(typeof(AsyncOperation));

    private volatile bool _isCancelled;

    protected AsyncOperation()
    {
      StatusSpeaker = new StatusSpeaker(new ListenersCollection());
    }

    public ListenersCollection StatusListeners
    {
      get { return StatusSpeaker.Listeners; }
    }

    public ILoadingListener LoadingListener { get; set; }

    public string Error { get; set; }

    public event Action OperationStarted;
    public event Action OperationFinished;

    protected StatusSpeaker StatusSpeaker { get; set; }

    protected bool IsCancelled
    {
      get { return _isCancelled; }
    }

    protected abstract void RunCore();

    public void Run(OperationContext syncContext)
    {
      Error = string.Empty;
      Log.InfoFormat("Operation started: {0}", GetType().Name);
      syncContext.Invoke(OnOperationStarted);
      if (LoadingListener != null)
        LoadingListener.LoadingStarted();
      // we must handle exceptions here
      // because of virtual thread can`t handle them
      try
      {
        RunCore();
      }
      catch (Exception ex)
      {
        Log.ErrorFormat("Operation error: {0} => {1}", GetType().Name, ex.Message);
        Error = ex.Message;
        StatusSpeaker.FatalError(ex);
        if (LoadingListener != null)
          LoadingListener.FatalError(ex);
      }
      finally
      {
        syncContext.Invoke(OnOperationFinished);
        Log.InfoFormat("Operation finished: {0}", GetType().Name);
        if (LoadingListener != null)
          LoadingListener.LoadingFinished(Error);
      }
    }

    public void CancelRequest()
    {
      _isCancelled = true;
    }

    protected virtual void OnOperationFinished()
    {
      if (OperationFinished != null)
        OperationFinished();
    }

    protected virtual void OnOperationStarted()
    {
      if (OperationStarted != null)
        OperationStarted();
    }
  }
}